package com.beautify.qrcode.utils;

import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.*;
import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit;

import javax.imageio.ImageIO;
import javax.imageio.stream.ImageOutputStream;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.ObjectUtil;
import com.beautify.qrcode.constants.TemplateConstant;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;

import com.beautify.qrcode.constants.QrCodeConstant;
import com.beautify.qrcode.constants.StructEnum;
import com.beautify.qrcode.struct.TemplateTypeService;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;

/**
 * 图元加载工具类
 *
 * @author keia
 * @since 2022/1/13 20:21
 */
@Slf4j
public class ImageLoadUtil {
    /**
     * 黑白默认背景图
     */
    private static byte[] backGroundImage;
    /**
     * 图元缓存
     */
    private static Cache<ImageKey,Image> cellImages;
    static {
        cellImages = CacheBuilder
            .newBuilder()
            .expireAfterWrite(10, TimeUnit.MINUTES)
            .build();
    }

    /**
     * 生产空白背景图
     * @return
     * @throws IOException
     */
    public InputStream createBackGroundImage() throws IOException {
        if(backGroundImage == null) {
            BufferedImage imgBar = new BufferedImage(200, 200, BufferedImage.TYPE_INT_BGR);
            Graphics2D g2 = (Graphics2D)imgBar.getGraphics();
            g2.fillRect(0,0,200,200);
            g2.setBackground(Color.WHITE);
            ByteArrayOutputStream bs = new ByteArrayOutputStream();
            ImageOutputStream imgOut = ImageIO.createImageOutputStream(bs);
            ImageIO.write(imgBar, QrCodeConstant.DEFAULT_IMAGE_TYPE, imgOut);
            g2.dispose();
            backGroundImage = bs.toByteArray();
        }
        return new ByteArrayInputStream(backGroundImage);
    }

    /**
     * 获取模板图片
     * @param templateType
     * @param structEnum
     * @param cellBit
     * @return
     * @throws Exception
     */
    public BufferedImage getTemplateImage(String templateType, StructEnum structEnum,int cellBit) throws Exception {
        String imageName;
        if(new TemplateTypeService().getTemplate(templateType) == null){
            throw new Exception("图元模板["+templateType+"]不存在");
        }else{
            imageName = TemplateConstant.TEMPLATE_PATH+templateType+File.separator+structEnum.getName();
        }
        Image scaledImage = getMapImage(imageName,cellBit*structEnum.getPoint().x,cellBit*structEnum.getPoint().y,"template");
        BufferedImage output = new BufferedImage(cellBit*structEnum.getPoint().x, cellBit*structEnum.getPoint().y,BufferedImage.TYPE_INT_BGR);
        //画图
        output.createGraphics().drawImage(scaledImage,0,0,null);
        return output;
    }

    private Image getMapImage(String imageName,int x,int y,String type) throws Exception{
        ImageKey key = new ImageKey(imageName, x, y, type);
        Image res = cellImages.get(key, new Callable<Image>() {
            @Override
            public Image call() throws Exception {
                log.info("图元[{}]放入缓存",imageName);
                File resource = FileUtil.file(imageName);
                if(!resource.exists()){
                    throw new Exception("图元["+imageName+"]不存在,无法加载");
                }
                try{
                    BufferedImage input = ImageIO.read(resource);
                    return input.getScaledInstance(x, y,Image.SCALE_DEFAULT);
                }catch (Exception e){
                    throw new Exception("图元["+imageName+"]加载失败");
                }

            }
        });

        return res;
    }

    @Data
    @AllArgsConstructor
    private static class ImageKey {
        String imageName;
        int x;
        int y;
        String type;

        @Override
        public int hashCode() {
            return imageName.hashCode() ^ x ^ y ^ type.hashCode();
        }

        @Override
        public boolean equals(Object obj) {
            if (obj == null) {
                return false;
            }
            if (!(obj instanceof ImageKey)) {
                return false;
            }
            ImageKey key = (ImageKey) obj;
            return ObjectUtil.equals(imageName, key.imageName) && x == key.x && y == key.y &&
                ObjectUtil.equals(type, key.type);
        }
    }
}
